package lamerman.lib.filesystem.rms;

import lamerman.lib.filesystem.rms.misc.Cluster;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;

public class RMSFSFile extends RMSFSNode {

    Cluster[] aClusters;
    public int length;

    /**This constructor should not be called by user, only for internal use by FS
    use RMSFSDir.makeFile instead*/
    RMSFSFile(RMSFSDir argParent, String argName, byte[] argBytes) throws RMSFSException, RMSFSFatalException {
        super(argParent, argName, argParent.rmsFsBase);

        write(argBytes);
    }

    /**This constructor should not be called by user, only for internal use by FS*/
    RMSFSFile(RMSFSDir argParent, String argName, Cluster[] argClusters, int argLength) {
        super(argParent, argName, argParent.rmsFsBase);

        aClusters = argClusters;
        length = argLength;
    }

    public byte[] read() throws RMSFSException, RMSFSFatalException {
        synchronized (rmsFsBase) {
            byte[] result = new byte[length];

            RecordStore rs = null;

            try {
                //current write position for argBytes
                int curOffset = 0;


                for (int clID = 0; clID < aClusters.length;) {
                    //Cluster has not been written yet, open RS to write all the clusters of this RS
                    int curRSID = aClusters[clID].rStoreID;

                    rs = RecordStore.openRecordStore(RMSFSBase.FILE_SPACE_NAME + curRSID, true);

                    //as long as record store remains the same
                    while (clID < aClusters.length && aClusters[clID].rStoreID == curRSID) {

                        byte[] aData = rs.getRecord(aClusters[clID].recordID);
                        System.arraycopy(aData, 0, result, curOffset, aClusters[clID].realLength);
                        curOffset += rmsFsBase.recordLength;
                        clID++;
                    }

                    rs.closeRecordStore();
                }

                return result;

            } catch (Exception e) {

                //most likelly here record store was not closed
                try {
                    if (rs != null) {
                        rs.closeRecordStore();
                    }
                } catch (RecordStoreException ex) {
                }

                //if record store or record set could not be opened for some reason
                //the file is deleted
                parent.deleteNode(name);

                e.printStackTrace();
                throw new RMSFSException("RecordStoreException at RMSFSFile.read");
            }
        }
    }

    final void write(byte[] argBytes) throws RMSFSException, RMSFSFatalException {
        synchronized (rmsFsBase) {
            length = argBytes.length;

            try {
                aClusters = rmsFsBase.allocateSpace(length);
            } catch (RMSFSException e) {
                e.printStackTrace();
                throw new RMSFSException("Can't write to the file due to lack of free space");
            }

            RecordStore rs = null;

            try {
                //current write position for argBytes
                int curOffset = 0;


                for (int clID = 0; clID < aClusters.length;) {
                    //Cluster has not been written yet, open RS to write all the clusters of this RS
                    int curRSID = aClusters[clID].rStoreID;

                    rs = RecordStore.openRecordStore(RMSFSBase.FILE_SPACE_NAME + curRSID, true);

                    //as long as record store remains the same
                    while (clID < aClusters.length && aClusters[clID].rStoreID == curRSID) {

                        writeBytesToRS(argBytes, curOffset, rs, aClusters[clID]);
                        curOffset += rmsFsBase.recordLength;
                        clID++;
                    }

                    rs.closeRecordStore();

                }
            } catch (Exception e) {

                //most likelly here record store was not closed
                try {
                    if (rs != null) {
                        rs.closeRecordStore();
                    }
                } catch (RecordStoreException ex) {
                }

                rmsFsBase.deallocateSpace(aClusters);

                e.printStackTrace();
                throw new RMSFSException("RecordStoreException at RMSFSFile.write");
            }
        }

    }

    public final boolean isDirectory() {
        return false;
    }

    private void writeBytesToRS(byte[] argBytes, int argOffset, RecordStore argRs, Cluster argCluster) throws RMSFSException {

        try {
            if (argCluster.recordID >= argRs.getNumRecords()) {
                //while there's not enough records, adds the records filled with arbitrary bytes
                while (argRs.getNumRecords() < argCluster.recordID) {
                    //fills it with a new array with the size of argRMSFS.recordLength
                    argRs.addRecord(new byte[rmsFsBase.recordLength], 0, rmsFsBase.recordLength);
                }
            }

            int curLength = argBytes.length - argOffset;
            //if there's more data left in the array than rmsFsBase.recordLength
            if (curLength > rmsFsBase.recordLength) {
                curLength = rmsFsBase.recordLength;

                //if data left is less than rmsFsBase.recordLength we expand, since some
                //devices like LG work incorrectly when you set to a record data with
                //different size
            } else if (curLength < rmsFsBase.recordLength) {
                byte[] aByte = new byte[rmsFsBase.recordLength];
                System.arraycopy(argBytes, argOffset, aByte, 0, curLength);
                argBytes = aByte;
                argOffset = 0;
            }

            //if records already exist, just replace existing record with these bytes
            argRs.setRecord(argCluster.recordID, argBytes, argOffset, rmsFsBase.recordLength);
            //remember the real size of the cluster
            argCluster.realLength = curLength;

        } catch (Exception e) {
            e.printStackTrace();
            throw new RMSFSException("RecordStoreNotOpenException at RMSFSFile.writeBytesToRS");
        }
    }
}
